package top.zhangjianyong.tools.service.etf;

import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import top.zhangjianyong.tools.entity.ETF;
import top.zhangjianyong.tools.entity.ETFData;
import top.zhangjianyong.tools.utils.DateTimeUtil;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @author ： zhangjianyong
 * @date ： 2022/9/29 3:39 PM
 */
@Slf4j
@Service
public class ETFJJServiceImpl implements ETFService {

    //https://fundf10.eastmoney.com/F10DataApi.aspx?type=lsjz&per=1000&page=1&code=515790
    private static final String URL = "https://fundf10.eastmoney.com/F10DataApi.aspx?type=lsjz&per=1000";

    private final static long sleepTime = 200;
    private final static long errSleepTime = 30 * 1000;

    /**
     * 获取ETF的页数
     */
    public int pageCount(String code) {

        String url = URL + "&page=" + 1 + "&code=" + code;
        log.info("[ETF 页数] url: {}", url);
        try (HttpResponse execute = HttpUtil.createGet(url).execute()) {
            String res = execute.body();

            log.info("[ETF 页数] res: {}", res);
            String patternPage = "(?<=pages:).*?(?=,)";
            Pattern rPage = Pattern.compile(patternPage);
            Matcher mPage = rPage.matcher(res);
            if (mPage.find()) {
                return Integer.parseInt(mPage.group(0));
            }
            return 0;
        }
    }

    public int pageCount(String code, int time) {
        try {
            if (time > 10) throw new RuntimeException("[请求页数] 失败");
            return pageCount(code);
        } catch (IllegalStateException | IORuntimeException e) {
            log.error(e.getMessage(), e);
            time = time + 1;
            log.info("[请求页数] 重试 {}", time);

            try {
                Thread.sleep(errSleepTime);
            } catch (InterruptedException ex) {
                log.error(e.getMessage(), ex);
                throw new RuntimeException(ex);
            }
            return pageCount(code, time);
        }

    }


    /**
     * 获取某一页数据
     */
    public List<ETFData> getRecords(int page, String code) {
        return analysisHtmlForETFValue(curPageHtml(page, code), code, null);
    }

    /**
     * 返回全部记录
     */
    public List<ETFData> getRecords(String code) throws Exception {

        List<ETFData> res = new ArrayList<>();
        ETFData etf = getETFDate(code, 0);
        if (etf == null) return null;
        int pages = pageCount(code, 0);
        for (int i = 1; i <= pages; i++) {
            List<ETFData> list = analysisHtmlForETFValue(curPageHtml(i, code), etf.getCode(), etf.getName());
            res.addAll(list);
            Thread.sleep(sleepTime);
        }
        return res;
    }


    /**
     * 返回html数据
     */
    public String curPageHtml(int page, String code) {
        return curPageHtml(page, code, 0);
    }

    public String curPageHtml(int page, String code, int time) {
        try {
            if (time > 10) throw new RuntimeException("请求分页数据错误");
            String url = URL + "&page=" + page + "&code=" + code;
            log.info("[ETF 数据] url: {}", url);
            try (HttpResponse execute = HttpUtil.createGet(url).execute()) {

                String res = execute.body();
                log.info("[ETF 数据] res: {} ", res);
                String patternHtml = "(?<=content:\").*?(?=\")";
                // 创建 Pattern 对象
                Pattern rHtml = Pattern.compile(patternHtml);
                // 现在创建 matcher 对象
                Matcher mHtml = rHtml.matcher(res);
                if (mHtml.find()) {
                    return mHtml.group(0);
                }
                return "";
            }
        } catch (IllegalStateException e) {
            log.error(e.getMessage(), e);
            time = time + 1;
            log.info("[ETF 数据] 重试 {}", time);
            try {
                Thread.sleep(errSleepTime);
            } catch (InterruptedException ex) {
                log.error(e.getMessage(), ex);
                throw new RuntimeException(ex);
            }
            return curPageHtml(page, code, time);
        }

    }

    /**
     * 解析html
     */
    public List<ETFData> analysisHtmlForETFValue(String html, String code, String name) {
        List<ETFData> list = new ArrayList<>();
        Document document = Jsoup.parse(html);
        Elements elements = document.body().getElementsByTag("tbody");
        Element tbody = elements.first();
        Elements trs = Objects.requireNonNull(tbody).children();

        for (Element element : trs) {
            ETFData etfData = new ETFData();
            etfData.setValueDate(DateTimeUtil.parseDate(element.child(0).text()));
            String valueText = element.child(1).text();
            String totalValueText = element.child(2).text();
            etfData.setValue(new BigDecimal(valueText));

            if (!StrUtil.isBlank(totalValueText))
                etfData.setTotalValue(new BigDecimal(totalValueText));

            String rate = element.child(3).text().replaceAll("%", "");
            if (StrUtil.isBlank(rate)) {
                etfData.setDailyRate(new BigDecimal(0));

            } else {
                etfData.setDailyRate(new BigDecimal(rate));
            }

            etfData.setCode(code);
            etfData.setName(name);
            list.add(etfData);
        }
        return list;

    }

    private static final String FUND_URL_JS = "http://fundgz.1234567.com.cn/js/";

    public ETF getETFInfo(String code) {
        /*获取最新数据*/
        String url = FUND_URL_JS + code + ".js";
        log.info("[ETF信息] url: {}", url);
        try (HttpResponse execute = HttpUtil.createGet(url).execute()) {

            String res = execute.body();
            log.info("[ETF信息] data: {}", res);

            String pattern = "(?<=jsonpgz\\().*(?=\\))";
            // 创建 Pattern 对象
            Pattern r = Pattern.compile(pattern);
            // 现在创建 matcher 对象
            Matcher m = r.matcher(res);
            JSONObject json = null;
            if (m.find()) {
                log.info(m.group(0));
                json = JSON.parseObject(m.group(0));
            }
            log.info("正则匹配数据：{}", json);
            ETF etf = new ETF();
            if (null == json) {
                throw new RuntimeException("获取最新净值失败: " + url);
            }
            etf.setCode(json.getString("fundcode"));
            etf.setValue(new BigDecimal(json.getString("gsz")));
            etf.setValueDate(DateTimeUtil.parseDateTime(json.getString("gztime"), DateTimeUtil.yyyyMMddHHmm));

            etf.setName(json.getString("name"));
            etf.setDailyRate(new BigDecimal(json.getString("gszzl")));

            return etf;
        }
    }

    public ETF getETF(String code) {
        if (code.startsWith("sh") || code.startsWith("sz")) {
            code = code.substring(2);
        }
        return getETF(code, 0);
    }

    public ETF getETF(String code, int time) {

        try {
            return getETFInfo(code);
        } catch (RuntimeException e) {
            log.error(e.getMessage(), e);
            if (time > 10) throw e;
            time = time + 1;
            log.info("[获取ETF] 重试 {}", time);
            try {
                Thread.sleep(errSleepTime);
            } catch (InterruptedException ex) {
                throw new RuntimeException(ex);
            }
            return getETF(code, time);

        }
    }

    public ETFData getETFDate(String code, int time) {
        ETF etf = getETF(code, time);
        return new ETFData(etf);

    }

    public static void main(String[] args) throws Exception {

        ETFJJServiceImpl etfJJService = new ETFJJServiceImpl();
        ETF etf = etfJJService.getETF("515790");
        System.out.println(JSONObject.toJSONString(etf));
    }


}
